// ffmpeg.worker.ts
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'

const baseURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.9/dist/esm'
const ffmpeg = new FFmpeg()
let isRunning = false
let ffmpegLogs = ''

// 解析时间字符串为秒数
function parseDuration(timeStr) {
  const parts = timeStr.split(':').map(Number)
  if (parts.length === 1) return parts[0]
  if (parts.length === 2) return parts[0] * 60 + parts[1]
  return parts[0] * 3600 + parts[1] * 60 + parts[2]
}

// 发送日志消息到主线程
function log(message) {
  ffmpegLogs += message + '\n'
  self.postMessage({ type: 'log', data: message })
}

// 发送进度消息到主线程
function updateProgress(progress, message) {
  self.postMessage({ type: 'progress', data: { progress, message } })
}

// 从FFmpeg文件系统中删除文件
async function removeFile(filePath) {
  try {
    if (typeof ffmpeg.FS === 'function') {
      ffmpeg.FS('unlink', filePath)
      log(`Deleted file: ${filePath}`)
    }
  } catch (error) {
    log(`Error deleting file ${filePath}: ${error.message}`)
  }
}

// 监控内存使用情况
function logMemoryUsage() {
  if (typeof performance !== 'undefined' && performance.memory) {
    const memory = performance.memory
    const usedMB = memory.usedJSHeapSize / 1024 / 1024
    const totalMB = memory.totalJSHeapSize / 1024 / 1024
    log(`Memory usage: ${usedMB.toFixed(2)}MB / ${totalMB.toFixed(2)}MB`)
  }
}

// 备选处理函数（当无法获取时长或内存不足时）
async function fallbackProcess() {
  const fixedSegments = 10
  const segmentDuration = 5

  log(`Fallback: processing with ${fixedSegments} segments of ${segmentDuration} seconds each`)

  try {
    // 重新加载FFmpeg以防崩溃
    await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
      wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
      workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
      wasmOptions: {
        initialMemory: 8192,
        maximumMemory: 32768
      }
    })

    // 本地文件已在input.mp4中，无需重新下载

    for (let i = 0; i < fixedSegments && isRunning; i++) {
      const startTime = i * segmentDuration
      const segmentName = `segment_${i}.mp4`
      const processedName = `processed_${i}.mp4`

      updateProgress(10 + (80 * i / fixedSegments), `Fallback: Processing segment ${i + 1}/${fixedSegments}`)

      // 提取片段
      await ffmpeg.exec([
        '-i', 'input.mp4',
        '-ss', startTime.toString(),
        '-t', segmentDuration.toString(),
        '-c', 'copy',
        segmentName
      ])

      // 转码片段（使用更安全的参数）
      await ffmpeg.exec([
        '-i', segmentName,
        '-c:v', 'libx264',
        '-crf', '23',
        '-preset', 'fast',
        '-movflags', '+faststart',
        '-c:a', 'aac',
        '-b:a', '128k',
        processedName
      ])

      removeFile(segmentName)
      logMemoryUsage()
    }

    if (!isRunning) return

    // 合并片段
    let segmentListContent = ''
    for (let i = 0; i < fixedSegments; i++) {
      segmentListContent += `file 'processed_${i}.mp4'\n`
    }
    await ffmpeg.writeFile('segments.txt', new TextEncoder().encode(segmentListContent))

    updateProgress(90, 'Merging segments...')
    // 使用更健壮的合并方法
    try {
      await ffmpeg.exec([
        '-f', 'concat',
        '-safe', '0',
        '-i', 'segments.txt',
        '-c:v', 'copy',
        '-c:a', 'copy',
        '-movflags', '+faststart+empty_moov+default_base_moof',
        'output.mp4'
      ])
    } catch (mergeError) {
      log(`Primary merge method failed: ${mergeError.message}`)
      log('Trying alternative merge method...')

      // 备选合并方法
      await ffmpeg.exec([
        '-i', 'segments.txt',
        '-filter_complex', '[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]',
        '-map', '[v]',
        '-map', '[a]',
        '-c:v', 'libx264',
        '-crf', '18',
        '-preset', 'ultrafast',
        '-c:a', 'aac',
        '-b:a', '64k',
        'output.mp4'
      ])
    }

    // 清理临时文件
    for (let i = 0; i < fixedSegments; i++) {
      removeFile(`processed_${i}.mp4`)
    }
    removeFile('segments.txt')
    removeFile('input.mp4')

    const data = await ffmpeg.readFile('output.mp4')
    updateProgress(100, 'Fallback transcoding completed!')
    self.postMessage({ type: 'complete', data })
  } catch (error) {
    self.postMessage({ type: 'error', data: `Fallback process failed: ${error.message}` })
  }
}

// 主处理函数（网络视频）
async function processVideo(videoURL) {
  try {
    isRunning = true
    updateProgress(0, 'Loading FFmpeg core...')

    await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
      wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
      workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
      wasmOptions: {
        initialMemory: 8192,
        maximumMemory: 32768
      }
    })

    ffmpeg.on('log', ({ type, message }) => {
      ffmpegLogs += `[${type}] ${message}\n`
      log(`[FFmpeg] ${message}`)
    })

    log('FFmpeg loaded successfully')
    updateProgress(5, 'Downloading video...')

    const videoData = await fetchFile(videoURL)
    await ffmpeg.writeFile('input.mp4', videoData)
    log('Video downloaded and written to FFmpeg filesystem')

    // 共用处理逻辑
    await processVideoCore()

  } catch (error) {
    const errorMsg = error.message
    log(`Error: ${errorMsg}`)

    if (errorMsg.includes('OOM') || errorMsg.includes('memory')) {
      log('Switching to fallback low-memory processing...')
      await fallbackProcess()
      return
    }

    self.postMessage({ type: 'error', data: errorMsg })
  } finally {
    isRunning = false
    try {
      await ffmpeg.terminate()
    } catch (e) {
      log(`Error releasing FFmpeg: ${e.message}`)
    }
  }
}

// 本地文件处理函数
async function processLocalFile(fileBuffer) {
  try {
    isRunning = true
    updateProgress(0, 'Loading FFmpeg core...')

    await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
      wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
      workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
      wasmOptions: {
        initialMemory: 8192,
        maximumMemory: 32768
      }
    })

    ffmpeg.on('log', ({ type, message }) => {
      ffmpegLogs += `[${type}] ${message}\n`
      log(`[FFmpeg] ${message}`)
    })

    log('FFmpeg loaded successfully')
    updateProgress(5, 'Processing local file...')

    // 将本地文件写入FFmpeg文件系统
    await ffmpeg.writeFile('input.mp4', new Uint8Array(fileBuffer))
    log('Local file written to FFmpeg filesystem')

    // 验证文件有效性
    /*const inputStats = ffmpeg.FS('stat', 'input.mp4')
    if (inputStats.size < 1024) {
      throw new Error(`Local file is too small (${inputStats.size} bytes), possibly corrupted`)
    }
    log(`Local file validated (size: ${inputStats.size} bytes)`)*/

    // 共用处理逻辑
    await processVideoCore()

  } catch (error) {
    const errorMsg = error.message
    log(`Local file processing error: ${errorMsg}`)

    if (errorMsg.includes('OOM') || errorMsg.includes('memory')) {
      log('Switching to fallback low-memory processing...')
      await fallbackProcess()
      return
    }

    self.postMessage({ type: 'error', data: errorMsg })
  } finally {
    isRunning = false
    try {
      await ffmpeg.terminate()
    } catch (e) {
      log(`Error releasing FFmpeg: ${e.message}`)
    }
  }
}

// 视频处理核心逻辑（网络/本地文件共用）
async function processVideoCore() {
  updateProgress(10, 'Analyzing video metadata...')
  try {
    await ffmpeg.exec(['-i', 'input.mp4'])
  } catch (e) {
    log('Video metadata analysis completed')
  }

  log('Extracting video duration...')
  const durationMatch = ffmpegLogs.match(
    /Duration:\s*(\d+:\d+:\d+\.\d+|\d+\.\d+s?)/i
  )

  if (!durationMatch) {
    log('Failed to get duration from logs, using fallback method')
    await fallbackProcess()
    return
  }

  const totalDuration = parseDuration(durationMatch[1].replace('s', ''))
  log(`Video duration: ${totalDuration} seconds`)

  const segmentDuration = 5
  const segmentCount = Math.ceil(totalDuration / segmentDuration)
  log(`Splitting into ${segmentCount} segments of ${segmentDuration} seconds each`)

  // 处理每个片段
  for (let i = 0; i < segmentCount && isRunning; i++) {
    const startTime = i * segmentDuration
    const segmentName = `segment_${i}.mp4`
    const processedName = `processed_${i}.mp4`

    updateProgress(10 + (80 * i / segmentCount), `Processing segment ${i + 1}/${segmentCount}`)

    // 提取片段
    log(`Extracting segment ${i + 1}/${segmentCount}`)
    await ffmpeg.exec([
      '-i', 'input.mp4',
      '-ss', startTime.toString(),
      '-t', segmentDuration.toString(),
      '-c', 'copy',
      segmentName
    ])

    // 释放输入视频占用的内存
    if (i === 0) {
      removeFile('input.mp4')
      log('Released input video from memory')
    }

    logMemoryUsage()

    // 转码片段
    log(`Transcoding segment ${i + 1}/${segmentCount}`)
    await ffmpeg.exec([
      '-i', segmentName,
      '-c:v', 'libx264',
      '-crf', '18',
      '-preset', 'fast',
      '-movflags', '+faststart',
      '-c:a', 'aac',
      '-b:a', '128k',
      processedName
    ])

    removeFile(segmentName)
    logMemoryUsage()
  }

  if (!isRunning) return

  // 合并片段
  let segmentListContent = ''
  for (let i = 0; i < segmentCount; i++) {
    segmentListContent += `file 'processed_${i}.mp4'\n`
  }
  await ffmpeg.writeFile('segments.txt', new TextEncoder().encode(segmentListContent))

  updateProgress(90, 'Merging segments...')
  log('Merging all segments into final video')
  await ffmpeg.exec([
    '-f', 'concat',
    '-safe', '0',
    '-i', 'segments.txt',
    '-c:v', 'copy',
    '-c:a', 'copy',
    '-movflags', '+faststart',
    'output.mp4'
  ])

  // 清理临时文件
  for (let i = 0; i < segmentCount; i++) {
    removeFile(`processed_${i}.mp4`)
  }
  removeFile('segments.txt')

  // 读取输出文件
  const data = await ffmpeg.readFile('output.mp4')
  updateProgress(100, 'Transcoding completed!')
  self.postMessage({ type: 'complete', data })
}

// 监听主线程消息
self.onmessage = (e) => {
  const { type, sourceType, data } = e.data
  if (type === 'start') {
    if (sourceType === 'url') {
      processVideo(data)
    } else if (sourceType === 'file') {
      processLocalFile(data)
    }
  } else if (type === 'cancel') {
    isRunning = false
    log('Transcoding cancelled by user')
    self.postMessage({ type: 'log', data: 'Transcoding cancelled by user' })
  }
}